package com.gmrz.uaf.protocol.v1.processor;

import com.gmrz.service.challengestore.ChallengeStoreException;
import com.gmrz.service.challengestore.ChallengeStoreService;
import com.gmrz.service.challengestore.IChallengeData;
import com.gmrz.uaf.common.Constants;
import com.gmrz.uaf.common.GuiceUtil;
import com.gmrz.uaf.common.UAFRuntimeException;
import com.gmrz.uaf.common.service.challengestore.UAFChallengeData;
import com.gmrz.uaf.common.service.challengestore.UAFChallengeStoreServiceProvider;
import com.gmrz.uaf.crypto.CryptoEngine;
import com.gmrz.uaf.crypto.spi.handlers.AttestationCertHandler;
import com.gmrz.uaf.db.DAOException;
import com.gmrz.uaf.db.DAOFactory;
import com.gmrz.uaf.db.UAFDAOFactory;
import com.gmrz.uaf.db.UAFDBConnectionMgr;
import com.gmrz.uaf.db.dao.AuthenticatorDAO;
import com.gmrz.uaf.db.dao.AuthenticatorSpecDAO;
import com.gmrz.uaf.db.dao.UasConfigDAO;
import com.gmrz.uaf.policy.PolicyManager;
import com.gmrz.uaf.protocol.v1.json.UAFSchemaBuilder;
import com.gmrz.uaf.protocol.v1.processor.exception.CertificateValidationException;
import com.gmrz.uaf.protocol.v1.processor.exception.PossibleReplayAttackException;
import com.gmrz.uaf.protocol.v1.processor.exception.SignatureValidationException;
import com.gmrz.uaf.protocol.v1.processor.exception.UAFErrorCode;
import com.gmrz.uaf.protocol.v1.schema.*;
import com.gmrz.uaf.server.ServerConfig;
import com.gmrz.util.db.DBUtil;
import com.google.common.base.Strings;
import com.google.gson.Gson;
import com.google.inject.Inject;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.security.SecureRandom;
import java.security.cert.Certificate;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.*;

public class UAFBaseProcessor {


    private static final Logger LOG = LogManager.getLogger(UAFBaseProcessor.class);

    protected UAFDBConnectionMgr dbutil = null;
    protected PolicyManager policyManager = null;
    protected ServerConfig serverConfig = null;
    private SecureRandom random = null;
    protected CryptoEngine cryptoEngine = null;
    private ChallengeStoreService mUafChallengeStoreService = null;

    @Inject
    public void setServerConfig(ServerConfig config) {
        this.serverConfig = config;
    }

    public ServerConfig getServerConfig() {
        return this.serverConfig;
    }

    @Inject
    private void setPolicyManager(PolicyManager pm) {
        this.policyManager = pm;
    }

    @Inject
    private void setTempDBUtil(UAFDBConnectionMgr dbutil) {
        this.dbutil = dbutil;
    }

    @Inject
    private void setSecureRandom(SecureRandom srandom) {
        this.random = srandom;
    }

    @Inject
    public void setCryptoEngine(CryptoEngine cryptoEngine) {
        this.cryptoEngine = cryptoEngine;
    }

    @Inject
    public void setUAFChallengeStoreService(ChallengeStoreService uafChallengeStoreService) {
        this.mUafChallengeStoreService = uafChallengeStoreService;
    }

    public ChallengeStoreService getmUafChallengeStoreService() {
        return mUafChallengeStoreService;
    }

    protected byte[] getRandom(int len) {
        byte[] retval = new byte[len];
        this.random.nextBytes(retval);
        return retval;
    }

    public AuthenticatorSpec getAuthenticatorSpec(String aaid) throws DAOException {
        LOG.debug("getAuthenticatorSpec:aaid[{}]", aaid);
        Connection conn = null;
        AuthenticatorSpec spec = null;
        try {
            UAFChallengeStoreServiceProvider challengeProvider = GuiceUtil.getProcessorInjector().getInstance(UAFChallengeStoreServiceProvider.class);
            String key = Constants.UAP_SPEC_ID + aaid;
            String[] f = new String[]{"json_data", "metadata_id", "aaid", "description", "status"};
            List<String> valueList = challengeProvider.get().getvalue(key, f);
            String json_data = valueList.get(0);
            String metadata_id = valueList.get(1);
            String aaidStr = valueList.get(2);
            String description = valueList.get(3);
            String status = valueList.get(4);
            if (!com.gmrz.util.Strings.isNullOrEmpty(json_data) &&
                    !com.gmrz.util.Strings.isNullOrEmpty(metadata_id) &&
                    !com.gmrz.util.Strings.isNullOrEmpty(aaidStr) &&
                    !com.gmrz.util.Strings.isNullOrEmpty(description) &&
                    !com.gmrz.util.Strings.isNullOrEmpty(status)) {
                spec = getSpec(json_data, metadata_id, aaidStr, description, status);
                if (null != spec) {
                    return spec;
                }
            }
            conn = UAFDBConnectionMgr.getConnection(true);
            AuthenticatorSpecDAO specdao = UAFDAOFactory.createAuthenticatorSpecDAO(conn);
            spec = specdao.getByAAID(aaid);
        } catch (Exception sqle) {
            LOG.error("Failed to access AuthenticatorSpec data, ", sqle);
            throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
        } finally {
            DBUtil.close(conn);
        }
        return spec;
    }

    private AuthenticatorSpec getSpec(String jsonData, String metadataId, String aaidStr, String description, String status) {
        AuthenticatorSpec spec = null;
        Gson gson = UAFSchemaBuilder.getGson();
        spec = gson.fromJson(jsonData, AuthenticatorSpec.class);
        if (spec != null) {
            spec.setID(metadataId);
            spec.setAAID(new AAID().parse(aaidStr));
            spec.setDescription(description);
            spec.setSpecStatus(Constants.AuthenticatorSpecStatus.forStatus(Integer.parseInt(status)));
        }
        return spec;
    }

    public Authenticator getAuthenticator(String appID, String userName, String aaid, String keyID,String transType) throws DAOException {
        Authenticator a = getAuthenticatorBase( appID,  userName,  aaid,  keyID);
        if (a == null) {
            String applicationID = this.getServerConfig().getApplicationID(appID,transType);
            if (this.getServerConfig().getTenantShare() && com.gmrz.util.Strings.isNotBlank(applicationID)) {
                List<String> shareAppIDList = getShareAppID(applicationID,transType);
                for(String shareAppID : shareAppIDList){
                    a = getAuthenticatorBase( shareAppID,  userName,  aaid,  keyID);
                    if (null != a) {
                        break;
                    }
                }
            }
        }
        return a;
    }

    protected Authenticator getAuthenticatorBase(String appID, String userName, String aaid, String keyID) throws DAOException {
        Connection conn = null;
        Authenticator a = null;
        try {
            conn = UAFDBConnectionMgr.getConnection(true);
            AuthenticatorDAO dao = UAFDAOFactory.createAuthenticatorDAO(conn);
            a = dao.findUser(appID, userName, aaid, keyID);
        } catch (SQLException sqle) {
            LOG.error("Failed to query user's registered info by appID[{}],userName[{}],aaid[{}],keyID[{}],error[{}]",
                    appID, userName, aaid, keyID, sqle);
            throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
        } finally {
            DBUtil.close(conn);
        }
        return a;
    }

    private HashMap<String, String> strtoHashMap(String strvalue) {
        strvalue = strvalue.substring(1, strvalue.length() - 1);
        String[] strs = strvalue.split("\\,");
        HashMap<String, String> m = new HashMap<String, String>();
        for (String s : strs) {
            String[] ms = s.split("\\=");
            if (ms.length >= 2) {
                m.put(ms[0].trim(), ms[1].replace(";", ",").trim());
            }
        }
        return m;
    }

    protected Set<Authenticator> getAuthenticatorsOnAllDevice(String userName, String appID) throws DAOException {
        LOG.debug("getAuthenticatorsOnAllDevice:Query user's registered info by userName=[{}],appID=[{}]",
                userName, appID);
        Connection conn = null;
        Set authenticators = new HashSet();
        try {
            conn = UAFDBConnectionMgr.getConnection(true);
//            AuthenticatorDAO dao = GuiceUtil.getProcessorInjector().getInstance(DAOFactory.class)
//                    .createAuthenticatorDAO(conn);
            AuthenticatorDAO dao = UAFDAOFactory.createAuthenticatorDAO(conn);
            authenticators = dao.findUserOnAllDevice(userName, appID);
        } catch (SQLException sqle) {
            LOG.error("getAuthenticatorsOnAllDevice:Query user's registered info on all devices by userName=[{}],appID=[{}],\n error[{}]",
                    userName, appID, sqle);
            throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
        } finally {
            DBUtil.close(conn);
        }
        return authenticators;
    }

    protected Set<Authenticator> getAuthenticatorsOnOneDevice(String userName, String appID, String deviceID,
                                                              String authType, String transType, int status)
            throws DAOException {
        LOG.debug("getAuthenticatorsOnOneDevice:Query user's registered info on one device by userName=[{}],appID=[{}]," +
                        "deviceID=[{}],authType=[{}],transType=[{}],status=[{}]",
                userName, appID, deviceID, authType, transType, status);
        Connection conn = null;
        try {
            conn = UAFDBConnectionMgr.getConnection(true);
//            AuthenticatorDAO dao = GuiceUtil.getProcessorInjector().getInstance(DAOFactory.class)
//                    .createAuthenticatorDAO(conn);
            AuthenticatorDAO dao = UAFDAOFactory.createAuthenticatorDAO(conn);
            return dao.findUserOnDevice(userName, appID, deviceID, authType, transType, status);

        } catch (SQLException sqle) {
            LOG.error("getAuthenticatorsOnOneDevice:Failed to connect database,", sqle);
            throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
        } finally {
            DBUtil.close(conn);
        }

    }

    public Set<Authenticator> getAuthenticatorsOnOneDeviceDEL(String userName, String appID, String deviceID,
                                                              String authType, String transType, int status)
            throws DAOException {
        LOG.debug("getAuthenticatorsOnOneDevice:Query user's registered info on one device by userName=[{}],appID=[{}]," +
                        "deviceID=[{}],authType=[{}],transType=[{}],status=[{}]",
                userName, appID, deviceID, authType, transType, status);
        Connection conn = null;
        try {
            conn = UAFDBConnectionMgr.getConnection(true);
            AuthenticatorDAO dao = GuiceUtil.getProcessorInjector().getInstance(DAOFactory.class)
                    .createAuthenticatorDAO(conn);
            // 判断是否为远程的人脸、声纹等远程的认证方式，认证方式码制以“1”开头的
            if (authType.compareTo(Constants.AuthType.REMOTE_FACEPRINT.getAuthType()) >= 0 && authType.compareTo(Constants.AuthType.CERT_FINGERPRINT.getAuthType()) < 0) {
                return dao.findUserNoDeviceDer(userName, appID, authType, transType, status);
            } else {
                return dao.findUserOnDeviceDer(userName, appID, deviceID, authType, transType, status);
            }
        } catch (SQLException sqle) {
            LOG.error("getAuthenticatorsOnOneDevice:Failed to connect database,", sqle);
            throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR, sqle);
        } finally {
            DBUtil.close(conn);
        }

    }

    protected void validateCertificate(List<Certificate> chain, AuthenticatorSpec spec, String aaid)
            throws CertificateValidationException {
        LOG.debug("validateCertificate aaid[{}]", aaid);
        if ((chain == null) || (chain.isEmpty())) {
            throw new CertificateValidationException(UAFErrorCode.PROTOCOL_ATTESTATION_CERT_VALIDATION_FAILED,
                    "Couldn't find attestation cert for Basic Full Attestation.");
        }

        boolean result = false;
        try {
            AttestationCertHandler handler = this.cryptoEngine.getAttestationCertHandler();
            if (handler != null)
                result = handler.validateCertificate(chain, spec.getRootCertificates(), aaid);
        } catch (Exception cpve) {
            LOG.error("Failed to validate certificate - ", cpve);
            throw new CertificateValidationException(UAFErrorCode.PROTOCOL_ATTESTATION_CERT_VALIDATION_FAILED, cpve);
        }
        if (!result)
            throw new CertificateValidationException(UAFErrorCode.PROTOCOL_ATTESTATION_CERT_VALIDATION_FAILED);
        LOG.debug("validateCertificate successfully!");
    }

    protected void validateSignature(int signAlgo, Certificate attestationCert, byte[] signedData, byte[] signature)
            throws SignatureValidationException {
        LOG.debug("validateSignature signAlgo[{}]", signAlgo);
        if (attestationCert == null) {
            throw new SignatureValidationException(UAFErrorCode.PROTOCOL_ATTESTATION_SIGNATURE_VALIDATION_FAILED,
                    "Couldn't find attestation cert for Basic Full Attestation.");
        }

        boolean result = false;
        try {
            result = this.cryptoEngine.verifySignature(signAlgo, attestationCert, signedData, signature);
        } catch (Exception cpve) {
            throw new SignatureValidationException(UAFErrorCode.PROTOCOL_ATTESTATION_SIGNATURE_VALIDATION_FAILED, cpve);
        }
        if (!result)
            throw new SignatureValidationException(UAFErrorCode.PROTOCOL_ATTESTATION_SIGNATURE_VALIDATION_FAILED);
        LOG.debug("validateSignature successfully!");
    }

    protected void validateSignature(int keyFormat, byte[] pubKey, int signAlgo, byte[] signedData, byte[] signature)
            throws SignatureValidationException {
        LOG.debug("validateSignature keyFormat[{}]", keyFormat);
        boolean result = false;
        try {
            result = this.cryptoEngine.verifySignature(keyFormat, pubKey, signAlgo, signedData, signature);
        } catch (Exception cpve) {
            throw new SignatureValidationException(UAFErrorCode.PROTOCOL_SIGNATURE_VALIDATION_FAILED, cpve);
        }
        if (!result)
            throw new SignatureValidationException(UAFErrorCode.PROTOCOL_SIGNATURE_VALIDATION_FAILED);
        LOG.debug("validateSignature successfully!");
    }


    protected Set<Authenticator> getAuthenticatorListForAlreadyRegistered(String username, String appID, String deviceID,
                                                                          String authType, String transType) throws DAOException {
        Set<Authenticator> registeredList = getAuthenticatorListForAlreadyRegisteredBase(username, appID, deviceID,authType, transType);
        // 如果未获取到已注册数据的话，就获取共享的注册数据
        registeredList = getShareRegisteredList(registeredList,appID, username,  deviceID, authType, transType);
        return registeredList;
    }

    protected Set<Authenticator> getAuthenticatorListForAlreadyRegisteredBase(String username, String appID, String deviceID,
                                                                          String authType, String transType) throws DAOException {
        LOG.debug("getAuthenticatorListForAlreadyRegistered begin [username=" + username + ",deviceID=" + deviceID +
                ",authType=" + authType + ",transType=" + transType + "]");
        Connection conn = null;
        Set<Authenticator> registeredList = null;
        if (!Strings.isNullOrEmpty(username)) {
            try {
                conn = UAFDBConnectionMgr.getConnection(true);
//				AuthenticatorDAO adao = GuiceUtil.getProcessorInjector().getInstance(DAOFactory.class)
//						.createAuthenticatorDAO(conn);
                AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);

                registeredList = adao.findUserOnDevice(username, appID, deviceID, authType, transType, Constants.AuthenticatorStatus.ACTIVE.getStatus());
            } catch (SQLException e) {
                LOG.error("Failed to get user's registered info by user[{}],deviceID[{}],authType[{}],transType[{}]", username, deviceID, authType, transType, e);
                throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR,
                        "Failed to get user's registered info for user[" + username + "]", e);
            } finally {
                DBUtil.close(conn);
            }
        }
        LOG.debug("getAuthenticatorListForAlreadyRegistered successfully!");
        return registeredList;
    }

    protected List<String> getShareAppID(String applicationID,String transType) throws DAOException {
        Connection conn = null;
        try {


            conn = UAFDBConnectionMgr.getConnection();
            UasConfigDAO uasConfigDAO = UAFDAOFactory.createUasConfigDAO(conn);
            return uasConfigDAO.getShareAppID(applicationID,transType);
        }catch (SQLException sqle){
            LOG.error("getShareAppID error",sqle);
            throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR);
        }finally {
            DBUtil.close(conn);
        }
    }
    protected Set<Authenticator> getShareRegisteredList(Set<Authenticator> registeredList,String appID,String hashedUserName, String deviceID,String auth,String transType) throws DAOException{
        if ((registeredList == null) || (registeredList.isEmpty())) {
            // 查询可信应用列表
            String applicationID = this.getServerConfig().getApplicationID(appID,transType);
            if (this.getServerConfig().getTenantShare() && com.gmrz.util.Strings.isNotBlank(applicationID)) {
                // 根据可信应用列表 和 业务类型 到 t_tenant_config 查询 appid
                List<String> shareAppIDList = getShareAppID(applicationID,transType);
                for(String shareAppID : shareAppIDList){
                    registeredList = getAuthenticatorListForAlreadyRegisteredBase(hashedUserName, shareAppID, deviceID,
                            auth, transType);
                    if (null != registeredList && !registeredList.isEmpty()) {
                        break;
                    }
                }
            }
        }
        return registeredList;
    }
    protected Set<Authenticator> getAuthenticatorListForAlreadyRegisteredNoDeviceId(String username, String appID,
                                                                                    String authType, String transType) throws DAOException {
        LOG.debug("getAuthenticatorListForAlreadyRegisteredNoDeviceId begin [username=" + username +
                ",authType=" + authType + ",transType=" + transType + "]");
        Connection conn = null;
        Set<Authenticator> registeredList = null;
        if (!Strings.isNullOrEmpty(username)) {
            try {
                conn = UAFDBConnectionMgr.getConnection(true);
//				AuthenticatorDAO adao = GuiceUtil.getProcessorInjector().getInstance(DAOFactory.class)
//						.createAuthenticatorDAO(conn);
                AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
                registeredList = adao.findUserNoDevice(username, appID, authType, transType, Constants.AuthenticatorStatus.ACTIVE.getStatus());
            } catch (SQLException e) {
                LOG.error("Failed to get user's registered info by user[{}],authType[{}],transType[{}]", username, authType, transType, e);
                throw new DAOException(UAFErrorCode.DB_DATABASE_CONNECTION_ERROR,
                        "Failed to get user's registered info for user[" + username + "]", e);
            } finally {
                DBUtil.close(conn);
            }
        }
        LOG.debug("getAuthenticatorListForAlreadyRegistered successfully!");
        return registeredList;
    }

    protected void checkForReplayAttack(ServerData serverData) throws ChallengeStoreException, PossibleReplayAttackException {
        LOG.debug("Check for potential replay attack");
        if (this.mUafChallengeStoreService == null) {
            LOG.error("Challenge store service manager not initialized");
            throw new ChallengeStoreException("Challenge Store service not initialized");
        }

        if ((serverData == null) || (serverData.getChallenge() == null)) {
            LOG.error("ServerData or challenge cannot be NULL");
            throw new ChallengeStoreException("ServerData or challenge cannot be NULL");
        }
        try {
            IChallengeData newData = new UAFChallengeData(serverData.getChallenge(), serverData.getTimestamp());
            // 去redis中查询是否已经存在了由【挑战值、时间戳】组成的key-value数据，如果存在则说明是重放攻击，如果不存在则放到redis中
            this.mUafChallengeStoreService.put(newData);

        } catch (ChallengeStoreException che) {
            if (che.getCause() != null) {
                String msg = che.getCause().getMessage();
                if ((msg != null) && (msg.equals("Possible Replay Attack"))) {
                    LOG.error("Possible replay attack for user: [" + serverData.getUserName() + "]");
                    throw new PossibleReplayAttackException(UAFErrorCode.SYS_POSSIBLE_ATTACK);
                }
            }
            throw che;
        }

        LOG.debug("Successfully completed check for replay attack");
    }

    protected void processExtensions(List<Extension> exts) {
        if ((exts != null) && (!exts.isEmpty())) {
            LOG.info("Found extensions in the response OperationHeader.");
            for (Extension x : exts)
                if (!x.getFailIfUnknown().booleanValue()) {
                    LOG.info("Found extension with 'fail_if_unknown' flag set to [false], ignoring the extension.");
                } else {
                    LOG.error("Found extension with 'fail_if_unknown' flag set to [true], rejecting the response.");
                    throw new UAFRuntimeException(
                            "Found extension with 'fail_if_unknown' flag set to [true], rejecting the response.");
                }
        }
    }

    protected void processTLSData(TLSData tlsData) {
        if (tlsData != null)
            LOG.info("TLSData found in the response FinalChallengeParams.");
    }

    protected boolean isAcceptedAaid(Policy p, String aaid) {
        if (p.getAccepted() == null)
            return false;
        List<List<MatchCriteria>> accepted = p.getAccepted();
        Iterator<List<MatchCriteria>> it = accepted.iterator();
        while (it.hasNext()) {
            MatchCriteria acceptedCriteria = it.next().get(0);
            if (acceptedCriteria.getAAID().size() > 0) {
                String acceptedAaid = acceptedCriteria.getAAID().get(0).toString();
                if (acceptedAaid.equals(aaid)) {
                    return true;
                }
            }
        }
        return false;

    }

    public MatchCriteria getAcceptedMatchCriteria(Policy policy) {
        if (policy.getAccepted().size() > 0) {
            List<List<MatchCriteria>> accepted = policy.getAccepted();
            List<MatchCriteria> disallowed = policy.getDisallowed();

            Iterator<List<MatchCriteria>> it = accepted.iterator();
            while (it.hasNext()) {
                MatchCriteria acceptedCriteria = it.next().get(0);
                if (acceptedCriteria.getAAID().size() > 0) {
                    String acceptedAaid = acceptedCriteria.getAAID().get(0).toString();
                    boolean isDisallowed = false;
                    if (disallowed != null) {
                        for (MatchCriteria disallowedCritria : disallowed) {
                            if (disallowedCritria.getAAID().size() != 0) {
                                String disallowedAaid = disallowedCritria.getAAID().get(0).toString();
                                if (acceptedAaid.equals(disallowedAaid)) {
                                    isDisallowed = true;
                                    LOG.info("aaid {} is disallowed", acceptedAaid);
                                    break;
                                }
                            }
                        }
                    }

                    if (!isDisallowed) {
                        return acceptedCriteria;
                    }
                }
            }
        }
        return null;
    }

    public void setFacetsAaid(RegistrationRequest request, String appID) {
        String appIdUrl = request.getHeader().getApplicationID();
        request.getHeader().setApplicationID(setFaceUrl(appIdUrl, appID));
    }

    public void setFacetsAaid(AuthenticationRequest request, String appID) {
        String appIdUrl = request.getOperationHeader().getApplicationID();
        request.getOperationHeader().setApplicationID(setFaceUrl(appIdUrl, appID));
    }

    public void setFacetsAaid(DeregisterationRequest request, String appID) {
        String appIdUrl = request.getOperationHeader().getApplicationID();
        request.getOperationHeader().setApplicationID(setFaceUrl(appIdUrl, appID));
    }

    private String setFaceUrl(String appIdUrl, String appID) {
//		int startIndex = appIdUrl.lastIndexOf("/");
//		String startStr = appIdUrl.substring(0,startIndex);
//		String endStr = appIdUrl.substring(startIndex);
//		String newAppIdUrl = startStr + "/"+ appID+endStr ;
        String newAppIdUrl = appIdUrl.replace(Constants.APP_ID, appID);
        return newAppIdUrl;
    }

    public void cacheOOBInfo(OOBInfo oobInfo, String key) throws ChallengeStoreException {
        Gson gson = new Gson();
        String oobInfoStr = gson.toJson(oobInfo);
        mUafChallengeStoreService.setRedisValue("uaf.oob." + key, this.getServerConfig().getScanCodeAuthCacheExpire(), oobInfoStr);
    }

    public OOBInfo getCacheOOBInfo(String key) throws ChallengeStoreException {
        Gson gson = new Gson();
        String oobInfoStr = mUafChallengeStoreService.getRedisValue("uaf.oob." + key);
        if (com.gmrz.util.Strings.isNotBlank(oobInfoStr)) {
            return gson.fromJson(oobInfoStr, OOBInfo.class);
        } else {
            return null;
        }

    }

    public void removeCacheOOBInfo(String key) throws ChallengeStoreException {
        mUafChallengeStoreService.deleteRedisValue("uaf.oob." + key);
    }


    public long getMaxCount() throws DAOException {
        Connection conn = null;
        try {
            String certNumStr = mUafChallengeStoreService.getRedisValue(Constants.CERT_NUM );
            if (com.gmrz.util.Strings.isNotBlank(certNumStr)) {
                return Long.parseLong(certNumStr);
            } else {

                conn = UAFDBConnectionMgr.getConnection();
                AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
                long certNum = adao.findMaxCount();
                boolean flag = mUafChallengeStoreService.tryGetDistributedLock(Constants.CERT_NUM, String.valueOf(certNum), 300000);
                if (flag) {
                    return certNum;
                } else {
                    String certNumString = mUafChallengeStoreService.getRedisValue(Constants.CERT_NUM);
                    if (com.gmrz.util.Strings.isNotBlank(certNumString)) {
                        return Long.parseLong(certNumString);
                    } else {
                        throw new ChallengeStoreException("Failed to get cert num from redis");
                    }
                }

            }
        } catch (SQLException sqle) {
            throw new DAOException(UAFErrorCode.DB_QIERY_CERTIFICATE_FAILED, sqle);
        } catch (ChallengeStoreException ce) {
            throw new DAOException(UAFErrorCode.PROTOCOL_FAILED_TO_GET_CERT,ce);
        } finally {
            DBUtil.close(conn);
        }
    }


    public long getMaxCount(String securityLevel) throws DAOException {
        Connection conn = null;
        try {
            String certNumStr = mUafChallengeStoreService.getRedisValue(Constants.CERT_NUM + "." +securityLevel);
            if (com.gmrz.util.Strings.isNotBlank(certNumStr)) {
                return Long.parseLong(certNumStr);
            } else {

                conn = UAFDBConnectionMgr.getConnection();
                AuthenticatorDAO adao = UAFDAOFactory.createAuthenticatorDAO(conn);
                long certNum = adao.findMaxCount(securityLevel);
                boolean flag = mUafChallengeStoreService.tryGetDistributedLock(Constants.CERT_NUM + "." +securityLevel, String.valueOf(certNum), 300000);
                if (flag) {
                    return certNum;
                } else {
                    String certNumString = mUafChallengeStoreService.getRedisValue(Constants.CERT_NUM + "." +securityLevel);
                    if (com.gmrz.util.Strings.isNotBlank(certNumString)) {
                        return Long.parseLong(certNumString);
                    } else {
                        throw new ChallengeStoreException("Failed to get cert num from redis");
                    }
                }

            }
        } catch (SQLException sqle) {
            throw new DAOException(UAFErrorCode.DB_QIERY_CERTIFICATE_FAILED, sqle);
        } catch (ChallengeStoreException ce) {
            throw new DAOException(UAFErrorCode.PROTOCOL_FAILED_TO_GET_CERT,ce);
        } finally {
            DBUtil.close(conn);
        }
    }

}
